package com.anonymous.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

import com.anonymous.client.Client;
import com.anonymous.message.ChatMsg;
import com.anonymous.message.HelloMsg;
import com.anonymous.message.Message;
import com.anonymous.message.PictureMsg;
import com.anonymous.message.PostUserListMsgArrayList;
import com.anonymous.message.ProfileMsg;
import com.anonymous.message.RequestProfileMsg;
import com.anonymous.model.Profile;
import com.anonymous.model.User;
import com.anonymous.model.UserListTableModel;
import com.anonymous.util.ConfidentialitySecurityUtils;
import com.anonymous.util.LoggerUtils;
import com.anonymous.util.NetworkUtils;

public class Driver {
	private LoggerUtils logger = new LoggerUtils();
	JFrame chatFrame;
	private Client client = null;
	private boolean isEditProfileWindowOpen = false;
	private boolean isViewProfileWindowOpen = false;
	private String loggedInUser = null;
	private JTable chatTable;
	private JTable userListTable;
	private JPopupMenu popup;

	private final int MAXFREEMSGLENGTH = 10;
	
	// Client KeyStore
	private KeyStore clientKeyStore;
	// Client KeyStore
	private KeyStore serverKeyStore;

	
	/*
	 * Since the Client only use the key pair for once
	 * and every time it get restarted Client will generate a 
	 * new pair of keys. During the time the Client key is used,
	 * it is kept in memory. No file is stored on the disk. Due to this
	 * reason and to be more convenient, we use a fixed pass phrase for all
	 * the client all time. 
	 */
	// passphrase of clientKeyStore
	private final String passphrase = "clientpw";

	private ConfidentialitySecurityUtils confidentialitySecurityUtils;
	private NetworkUtils networkUtils;
	private KeyPair clientKeyPair;
	private X509Certificate clientCertificate;

	// private Map<String, ClientMessageAccepter> ClientsThreadsMap; for final
	// release
	private String serverIP;

	private Map<String, DefaultTableModel> conversations = new HashMap<String, DefaultTableModel>();
	private Map<String, Socket> socketMap = new HashMap<String, Socket>();

	private Profile profile = new Profile("", "", "");
	private Map<String, Boolean> profileSharingMap = new HashMap<String, Boolean>();

	private JScrollPane chatScrollPane;
	private JPanel chatPanel;
	private JButton okButton;
	private JButton loginButton;
	private JButton sendMessageButton;

	private DefaultTableModel chatTableModel = new DefaultTableModel() {
		private static final long serialVersionUID = 1L;

		public boolean isCellEditable(int nRow, int nCol) {
			return false;
		}
	};

	// kicks off a client user interface
	public static void main(String[] args) throws Exception {
		// set operating system specific look and feel
		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		// instantiate new driver class
		@SuppressWarnings("unused")
		Driver driver = new Driver();

	}

	// constructor
	public Driver() throws Exception {
		confidentialitySecurityUtils = new ConfidentialitySecurityUtils();
		networkUtils = new NetworkUtils();
		clientKeyPair = confidentialitySecurityUtils.generateCustomKeyPair();
		serverKeyStore = confidentialitySecurityUtils.getServerKeystore();
		clientKeyStore = null;
		initChooseServerIp();
	}

	private void initChooseServerIp() {
		final JFrame serverIpFrame = new JFrame("Server IP");
		serverIpFrame.setResizable(false);
		serverIpFrame.setSize(200, 100);
		serverIpFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				System.exit(0);
			}
		});

		// center frame
		serverIpFrame.setLocationRelativeTo(null);

		// set up container
		Container serverIpContent = serverIpFrame.getContentPane();

		// set up panel
		JPanel serverIpPanel = new JPanel();

		// create server ip text field
		final JTextField ipTextField = new JTextField();
		ipTextField.setColumns(10);
		ipTextField.addKeyListener(new KeyListener() {

			@Override
			public void keyPressed(KeyEvent arg0) {

			}

			@Override
			public void keyReleased(KeyEvent arg0) {
				if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
					okButton.doClick();
				}
			}

			@Override
			public void keyTyped(KeyEvent arg0) {

			}

		});

		// create login button
		okButton = new JButton("OK");
		okButton.addActionListener(new ActionListener() {

			// attempt login
			public void actionPerformed(ActionEvent e) {

				serverIP = ipTextField.getText();
				try {
					client = new Client(serverIP, serverKeyStore);
					if (!client.connect()) {
						System.exit(0);
					}
				} catch (Exception exception) {
					exception.printStackTrace();
				}
				serverIpFrame.dispose();
				// kick off login process
				initChatLogin();
			}
		});

		serverIpPanel.add(ipTextField);
		serverIpPanel.add(okButton);

		// add panel to content
		serverIpContent.add(serverIpPanel);

		// show frame
		serverIpFrame.setVisible(true);

	}

	// GUI for chat login screen
	private void initChatLogin() {
		final JFrame loginFrame = new JFrame("SecureChat Client");
		loginFrame.setResizable(false);
		loginFrame.setSize(250, 200);
		loginFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				System.exit(0);
			}
		});

		// center frame
		loginFrame.setLocationRelativeTo(null);

		// set up container
		Container loginContent = loginFrame.getContentPane();

		// set up panel
		JPanel loginPanel = new JPanel();

		// create username text field
		final JTextField userNameTextField = new JTextField();
		userNameTextField.setColumns(10);

		// create password text field
		final JPasswordField passwordTextField = new JPasswordField();
		passwordTextField.setColumns(10);
		passwordTextField.addKeyListener(new KeyListener() {

			@Override
			public void keyPressed(KeyEvent arg0) {

			}

			@Override
			public void keyReleased(KeyEvent arg0) {
				if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
					loginButton.doClick();
				}
			}

			@Override
			public void keyTyped(KeyEvent arg0) {

			}

		});

		// create login button
		loginButton = new JButton("Login");
		loginButton.addActionListener(new ActionListener() {

			// attempt login
			public void actionPerformed(ActionEvent e) {
				try {
					// create socket to server
					String userName = userNameTextField.getText();
					clientCertificate = confidentialitySecurityUtils.generateSelfSignedX509Certificate(clientKeyPair,userName);

					clientKeyStore = confidentialitySecurityUtils.setupClientKeyStore(
							passphrase, clientKeyPair, clientCertificate);
					try {
						if (client.login(
								userName,
								String.valueOf(passwordTextField.getPassword()),
								clientCertificate)) {
							loggedInUser = userName;
							loginFrame.dispose();

							new InitialServerMessageAccepter().execute();
							new InitialClientConnectionAccepter().execute();
							initChatInterface();
						} else {
							JOptionPane.showMessageDialog(chatFrame, "Login failed!!!");
							System.out.println("failed login");
						}
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				} catch (Exception e1) {
					e1.printStackTrace();
				}

			}
		});

		// create create free account button
		JButton createFreeAccountButton = new JButton("Create Free Account");
		createFreeAccountButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				try {
					// Execute when button is pressed
					try {

						String userName = userNameTextField.getText();
						clientCertificate = confidentialitySecurityUtils.generateSelfSignedX509Certificate(clientKeyPair,userName);

						clientKeyStore = confidentialitySecurityUtils.setupClientKeyStore(
								passphrase, clientKeyPair, clientCertificate);
						if (client.register(
								userName,
								String.valueOf(passwordTextField.getPassword()),false,
								clientCertificate)) {
							loggedInUser = userName;
							loginFrame.dispose();

							new InitialServerMessageAccepter().execute();
							new InitialClientConnectionAccepter().execute();
							initChatInterface();
						} else {
							JOptionPane.showMessageDialog(chatFrame, "Register failed!!!");
							System.out.println("failed register");
						}
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				} catch (Exception e1) {
					e1.printStackTrace();
				}

			}
		});

		// create create paid account button
		JButton createPaidAccountButton = new JButton("Create Paid Account");
		createPaidAccountButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				try {
					// Execute when button is pressed
					try {
						String userName = userNameTextField.getText();
						clientCertificate = confidentialitySecurityUtils.generateSelfSignedX509Certificate(clientKeyPair,userName);

						clientKeyStore = confidentialitySecurityUtils.setupClientKeyStore(
								passphrase, clientKeyPair, clientCertificate);
						if (client.register(
								userName,
								String.valueOf(passwordTextField.getPassword()),true,
								clientCertificate)) {
							loggedInUser = userName;
							loginFrame.dispose();

							new InitialServerMessageAccepter().execute();
							new InitialClientConnectionAccepter().execute();
							initChatInterface();
						} else {
							JOptionPane.showMessageDialog(chatFrame, "Register failed!!!");
							System.out.println("failed register");
							
						}
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				} catch (Exception e1) {
					e1.printStackTrace();
				}

			}
		});

		// add components to panel
		loginPanel.add(new JLabel("Username"));
		loginPanel.add(userNameTextField);
		loginPanel.add(new JLabel("Password"));
		loginPanel.add(passwordTextField);
		loginPanel.add(loginButton);
		loginPanel.add(createFreeAccountButton);
		loginPanel.add(createPaidAccountButton);

		// add panel to content
		loginContent.add(loginPanel);
		System.out.println("Client Acceptor started");

		// show frame
		loginFrame.setVisible(true);
	}

	// GUI for editing user profile
	private void initEditProfileInterface() {
		final JFrame editProfileFrame = new JFrame("Edit Profile");
		editProfileFrame.setSize(200, 200);
		editProfileFrame.setResizable(false);
		// center frame
		editProfileFrame.setLocationRelativeTo(null);
		editProfileFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				isEditProfileWindowOpen = false;
			}
		});

		// set up container
		Container editProfileContent = editProfileFrame.getContentPane();

		// set up panel
		JPanel editProfilePanel = new JPanel();

		// create name text field
		final JTextField nameTextField = new JTextField();
		nameTextField.setText(profile.getName());
		nameTextField.setColumns(10);

		// create gender text field
		final JTextField genderTextField = new JTextField();
		genderTextField.setText(profile.getGender());
		genderTextField.setColumns(10);

		// create age text field
		final JTextField ageTextField = new JTextField();
		ageTextField.setText(profile.getAge());
		ageTextField.setColumns(10);

		// add fields to profile panel
		editProfilePanel.add(new JLabel("Name"));
		editProfilePanel.add(nameTextField);
		editProfilePanel.add(new JLabel("Gender"));
		editProfilePanel.add(genderTextField);
		editProfilePanel.add(new JLabel("Age"));
		editProfilePanel.add(ageTextField);

		// add save and cancel buttons
		JButton saveProfileButton = new JButton("Save");
		saveProfileButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				// Execute when button is pressed
				profile.setAge(ageTextField.getText());
				profile.setGender(genderTextField.getText());
				profile.setName(nameTextField.getText());
				editProfileFrame.dispose();
				isEditProfileWindowOpen = false;
			}
		});

		JButton cancelProfileButton = new JButton("Cancel");
		cancelProfileButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				// Execute when button is pressed
				editProfileFrame.dispose();
				isEditProfileWindowOpen = false;
			}
		});

		// add buttons to panel
		editProfilePanel.add(saveProfileButton);
		editProfilePanel.add(cancelProfileButton);

		// add panel to content container
		editProfileContent.add(editProfilePanel);

		// show frame, always on top
		editProfileFrame.setAlwaysOnTop(true);
		editProfileFrame.setVisible(true);

	}

	// GUI for viewing another user's profile
	private void initViewProfileInterface(String username, Profile profile) {
		final JFrame viewProfileFrame = new JFrame("Profile");
		viewProfileFrame.setSize(200, 220);
		viewProfileFrame.setResizable(false);
		// center frame
		viewProfileFrame.setLocationRelativeTo(null);
		viewProfileFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				isViewProfileWindowOpen = false;
			}
		});

		// set up container
		Container viewProfileContent = viewProfileFrame.getContentPane();

		// set up panel
		JPanel viewProfilePanel = new JPanel();

		// create name text field
		JTextField nameTextField = new JTextField(profile.getName());
		nameTextField.setColumns(10);
		nameTextField.setEnabled(false);

		// create gender text field
		JTextField genderTextField = new JTextField(profile.getGender());
		genderTextField.setColumns(10);
		genderTextField.setEnabled(false);

		// create age text field
		JTextField ageTextField = new JTextField(profile.getAge());
		ageTextField.setColumns(10);
		ageTextField.setEnabled(false);

		// add fields to profile panel
		viewProfilePanel.add(new JLabel(username));
		viewProfilePanel.add(new JLabel("Name"));
		viewProfilePanel.add(nameTextField);
		viewProfilePanel.add(new JLabel("Gender"));
		viewProfilePanel.add(genderTextField);
		viewProfilePanel.add(new JLabel("Age"));
		viewProfilePanel.add(ageTextField);

		// add panel to content container
		viewProfileContent.add(viewProfilePanel);

		// show frame, always on top
		viewProfileFrame.setAlwaysOnTop(true);
		viewProfileFrame.setVisible(true);

	}

	// after login success, create GUI for chatting with users
	private void initChatInterface() throws Exception {

		// set up frame
		String windowTitle = "SecureChat Client - ";
		if(client.isPaidUser(loggedInUser)){
			windowTitle += "Paid User"; 
		}
		else{
			windowTitle += "Free User";
		}
		chatFrame = new JFrame(windowTitle);
		chatFrame.setSize(1000, 550);
		chatFrame.setResizable(false);
		chatFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {

				try {
					client.logout();
				} catch (Exception e1) {
					e1.printStackTrace();
				}

				System.exit(0);
			}
		});

		// center frame
		chatFrame.setLocationRelativeTo(null);

		// Create the menu bar.
		JMenuBar menuBar = new JMenuBar();
		JMenu menu = new JMenu("File");
		JMenuItem editProfileMenuItem = new JMenuItem("Edit Profile");
		editProfileMenuItem.addActionListener(new ActionListener() {

			// for editing profile
			public void actionPerformed(ActionEvent e) {
				if (isEditProfileWindowOpen == false) {
					initEditProfileInterface();
					isEditProfileWindowOpen = true;
				}

			}
		});

		JMenuItem PayMenuItem = new JMenuItem("Pay!!");
		PayMenuItem.addActionListener(new ActionListener() {

			// for editing profile
			public void actionPerformed(ActionEvent e) {
				if(client.Pay()){
					String windowTitle = "SecureChat Client - Paid User";
					chatFrame.setTitle(windowTitle);

					JOptionPane.showMessageDialog(chatFrame, "Thank you for your payment!!!!!");
				}
				else{
					JOptionPane.showMessageDialog(chatFrame, "You have already paid.");
				}
			}
		});
		
		
		
		JMenuItem logoutMenuItem = new JMenuItem("Logout");
		logoutMenuItem.addActionListener(new ActionListener() {

			// log out menu button
			public void actionPerformed(ActionEvent e) {
				try {
					client.logout();
					System.exit(0);

				} catch (Exception e1) {
					e1.printStackTrace();
				}

			}
		});
		menu.add(editProfileMenuItem);
		menu.add(PayMenuItem);
		menu.add(logoutMenuItem);
		menuBar.add(menu);

		chatFrame.setJMenuBar(menuBar);

		// set up container
		Container chatContent = chatFrame.getContentPane();
		chatContent.setLayout(new BorderLayout());

		// set up chat panel
		chatPanel = new JPanel();
		chatPanel.setBackground(Color.orange);

		// set up chat table
		chatTableModel.addColumn("Timestamp");
		chatTableModel.addColumn("Username");
		chatTableModel.addColumn("Message");
		chatTable = new JTable(chatTableModel);
		chatTable.setEnabled(false);
		chatScrollPane = new JScrollPane(chatTable);

		userListTable = new JTable(null);
		userListTable.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				// Left mouse click to select user. **user must be selected to
				// chat with them
				if (SwingUtilities.isLeftMouseButton(e)) {
					// get the coordinates of the mouse click
					Point p = e.getPoint();

					// get the row index that contains that coordinate
					int rowNumber = userListTable.rowAtPoint(p);

					// Get the ListSelectionModel of the JTable
					ListSelectionModel model = userListTable
							.getSelectionModel();

					// set the selected interval of rows. Using the "rowNumber"
					// variable for the beginning and end selects only that one
					// row.
					model.setSelectionInterval(rowNumber, rowNumber);

					// change chat pane
					changeConversationPane((String) userListTable.getModel()
							.getValueAt(rowNumber, 0));
				}
				// Right mouse click for showing menu to view profile of
				// selected user
				else if (SwingUtilities.isRightMouseButton(e)) {
					if (isViewProfileWindowOpen == false) {
						// get the coordinates of the mouse click
						Point mouseAbPostion = e.getLocationOnScreen();
						Point p = e.getPoint();
						

						// get the row index that contains that coordinate
						int rowNumber = userListTable.rowAtPoint(p);

						// Get the ListSelectionModel of the JTable
						ListSelectionModel model = userListTable
								.getSelectionModel();

						// set the selected interval of rows. Using the
						// "rowNumber"
						// variable for the beginning and end selects only that
						// one
						// row.
						model.setSelectionInterval(rowNumber, rowNumber);

						final String userName = (String) (userListTable
								.getModel().getValueAt(rowNumber, 0));
						popup = new JPopupMenu();
						JMenuItem viewProfileMenuItem = new JMenuItem(
								"View Profile");
						viewProfileMenuItem
								.addActionListener(new ActionListener() {

									public void actionPerformed(ActionEvent e) {
										RequestProfileMsg requestProfileMsg = new RequestProfileMsg();
										requestProfileMsg.setFromUserName(loggedInUser);
										try {
											if (socketMap.get(userName) != null) {
												networkUtils.send(requestProfileMsg, socketMap.get(userName));
											} else {
												new InitialClientConnectionRequester(
														requestProfileMsg).execute();
											}
											
										} catch (Exception e1) {
											e1.printStackTrace();
											popup.setVisible(false);
										}
										popup.setVisible(false);
									}
								});
						JMenuItem shareProfileMenuItem = null;
						if (profileSharingMap.get(userName) == null
								|| profileSharingMap.get(userName)
										.booleanValue() == false) {
							shareProfileMenuItem = new JMenuItem(
									"Share My Profile with " + userName);
							shareProfileMenuItem
									.addActionListener(new ActionListener() {

										public void actionPerformed(
												ActionEvent e) {
											profileSharingMap.put(userName,
													new Boolean(true));
											popup.setVisible(false);
										}
									});
						} else {
							shareProfileMenuItem = new JMenuItem(
									"Don't Share My Profile with " + userName);
							shareProfileMenuItem
									.addActionListener(new ActionListener() {

										public void actionPerformed(
												ActionEvent e) {
											profileSharingMap.put(userName,
													new Boolean(false));
											popup.setVisible(false);
										}
									});

						}
						
						JMenuItem cancelMenuItem = new JMenuItem(
								"Cancel");
						cancelMenuItem
								.addActionListener(new ActionListener() {

									@Override
									public void actionPerformed(ActionEvent e) {
										popup.setVisible(false);
									}
								
								});

						popup.add(viewProfileMenuItem);
						popup.add(shareProfileMenuItem);
						popup.add(cancelMenuItem);
						popup.setLocation(mouseAbPostion);
						popup.setVisible(true);
					}
				}
			}
		});

		JScrollPane userListScrollPane = new JScrollPane(userListTable);

		// add chatLogTable to chatPanel
		chatPanel.add(userListScrollPane);

		// add chatLogTable to chatPanel
		chatPanel.add(chatScrollPane);

		// create send message panel
		JPanel sendMessagePanel = new JPanel();

		// create message text field
		final JTextField messageTextField = new JTextField(
				"Enter your message here...");
		messageTextField.setColumns(50);
		messageTextField.addKeyListener(new KeyListener() {

			@Override
			public void keyPressed(KeyEvent arg0) {

			}

			@Override
			public void keyReleased(KeyEvent arg0) {
				if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
					sendMessageButton.doClick();
				}
			}

			@Override
			public void keyTyped(KeyEvent arg0) {

			}

		});

		// create send message button
		sendMessageButton = new JButton("Send");
		sendMessageButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				// Execute when button is pressed
				String messageToSend = messageTextField.getText();
				if(messageToSend.length() == 0)return;
				if(messageToSend.length() > MAXFREEMSGLENGTH){
					if(!client.isPaidUser(loggedInUser)){
						JOptionPane.showMessageDialog(chatFrame, "Stop Being CHEAP!!! Pay to send longer messages.");
						return;
					}
				}
				ChatMsg chatMsg = new ChatMsg();
				chatMsg.setFromUserName(loggedInUser);
				chatMsg.setMessage(messageToSend);
				messageTextField.setText("");
				int selectedRow = userListTable.getSelectedRow();
				String Requestee = null;
				try{
				Requestee = (String) userListTable.getValueAt(
						selectedRow, 0);
				}
				catch(Exception e2){
					//exception if no one is selected
					return;
				}
				// Look to see whether it's in the socketMap.
				try {
					if (socketMap.get(Requestee) != null) {

						networkUtils.send(chatMsg, socketMap.get(Requestee));
						String message = "Send to: "+Requestee;
						logger.LogNormal(message);
						updateConversationPane(new Object[] {
								Calendar.getInstance().getTime().toString(),
								loggedInUser, messageToSend }, Requestee);
					} else {
						new InitialClientConnectionRequester(chatMsg).execute();
					}
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});

		// create send picture button
		JButton sendPictureButton = new JButton("Send Picture");
		sendPictureButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				// Execute when button is pressed
				if(!client.isPaidUser(loggedInUser)){
					JOptionPane.showMessageDialog(chatFrame, "Stop Being CHEAP!!! Pay to send pictures.");
					return;
				}
				
				
				
				int selectedRow = userListTable.getSelectedRow();
				String ipAddress = (String) userListTable.getValueAt(
						selectedRow, 2);
				final String userName = (String) userListTable.getValueAt(
						selectedRow, 0);

				final JPanel pictureUI = new JPanel();
				final JFileChooser fc = new JFileChooser();
				fc.setAcceptAllFileFilterUsed(false);
				int returnVal = fc.showDialog(pictureUI, "Attach");
				// choosePictureFrame.dispose();
				if (JFileChooser.APPROVE_OPTION == returnVal) {
					File file = fc.getSelectedFile();
					BufferedImage image = null;
					try {
						image = ImageIO.read(file);
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					final BufferedImage resized = new BufferedImage(200, 200,
							image.getType());
					Graphics2D g = resized.createGraphics();
					g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
							RenderingHints.VALUE_INTERPOLATION_BILINEAR);
					g.drawImage(image, 0, 0, 200, 200, 0, 0, image.getWidth(),
							image.getHeight(), null);
					g.dispose();
					ImageIcon imageIcon = new ImageIcon();
					imageIcon.setImage(resized);
					JLabel label = new JLabel();
					label.setIcon(imageIcon);
					JPanel jp = new JPanel();
					jp.add(label);

					final JFrame picturePreviewFrame = new JFrame(
							"Picture to Send");
					picturePreviewFrame.setSize(400, 400);
					picturePreviewFrame.setLocationRelativeTo(null);
					// create ok/cancel buttons
					JButton pictureConfirmButton = new JButton("Send");
					pictureConfirmButton
							.addActionListener(new ActionListener() {
								@Override
								public void actionPerformed(ActionEvent e) {
									// do stuff to send picture
									ImageIcon imageIconToSend = new ImageIcon();
									imageIconToSend.setImage(resized);
									PictureMsg picMsg = new PictureMsg();
									picMsg.setFromUserName(loggedInUser);
									picMsg.setImage(imageIconToSend);
									int selectedRow = userListTable
											.getSelectedRow();
									String Requestee = null;
									try{
									Requestee = (String) userListTable
											.getValueAt(selectedRow, 0);
									}
									catch(Exception e2){
										picturePreviewFrame.dispose();
										return;
									}
									// Look to see whether it's in the
									// socketMap.
									try {
										if (socketMap.get(Requestee) != null) {

											networkUtils.send(picMsg,
													socketMap.get(Requestee));

											updateConversationPane(
													new Object[] {
															Calendar.getInstance()
																	.getTime()
																	.toString(),
															loggedInUser,
															"Picture Sent" },
													userName);
										} else {

											new InitialClientConnectionRequester(
													picMsg).execute();
										}
										picturePreviewFrame.dispose();
									} catch (Exception e1) {
										e1.printStackTrace();
									}

								}

							});
					JButton pictureCancelButton = new JButton("Cancel");
					pictureCancelButton.addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							picturePreviewFrame.dispose();
						}

					});

					// add buttons
					jp.add(pictureConfirmButton);
					jp.add(pictureCancelButton);

					// Add content to the window.
					picturePreviewFrame.add(jp);

					// Display the window.
					// picturePreviewFrame.pack();
					picturePreviewFrame.setVisible(true);

				} else {

				}

			}
		});

		// add message text field to message panel
		sendMessagePanel.add(messageTextField);

		// add send message button to message panel
		sendMessagePanel.add(sendMessageButton);
		// add send picture button to message panel
		sendMessagePanel.add(sendPictureButton);

		// add send message panel to content container
		chatContent.add(sendMessagePanel, BorderLayout.SOUTH);

		// add components to content container
		chatContent.add(chatPanel, BorderLayout.NORTH);

		// show frame
		chatFrame.setVisible(true);

		// update userlist periodically
		// new UserListUpdater().execute();

		// start accepting peer to peer (client to client) connections

	}

	class InitialClientConnectionRequester extends SwingWorker<Void, Object> {

		private String remoteClientIP;
		private KeyStore remoteClientKeyStore;
		private SSLContext sslContext;
		private Message message;
		private String Requestee;
		private final int initPort = 6678;
		private final int port = 6679;
		ConfidentialitySecurityUtils confidentialitySecurityUtils;

		public InitialClientConnectionRequester(Message msg) throws Exception {
			confidentialitySecurityUtils = new ConfidentialitySecurityUtils();
			message = msg;

			int selectedRow = userListTable.getSelectedRow();
			Requestee = (String) userListTable.getValueAt(selectedRow, 0);

			remoteClientIP = (String) userListTable.getValueAt(selectedRow, 2);
			X509Certificate cert = (X509Certificate) userListTable.getValueAt(
					selectedRow, 1);

			setRemoteClientKeyStore(cert);
			sslContext = setupSSLContext();
		}

		private void setRemoteClientKeyStore(X509Certificate cert)
				throws Exception {
			remoteClientKeyStore = KeyStore.getInstance("JKS");
			// GENERATE AN EMPTY clientKeyStore
			remoteClientKeyStore.load(null, passphrase.toCharArray());

			// Add the certificate
			remoteClientKeyStore.setCertificateEntry("clientCertificate", cert);
		}

		private SSLContext setupSSLContext() {

			SSLContext sslContext = null;
			try {
				TrustManagerFactory tmf = TrustManagerFactory
						.getInstance("SunX509");
				tmf.init(remoteClientKeyStore);

				KeyManagerFactory kmf = KeyManagerFactory
						.getInstance("SunX509");
				kmf.init(clientKeyStore, passphrase.toCharArray());

				sslContext = SSLContext.getInstance("TLS");

				SecureRandom secureRandom = new SecureRandom();
				secureRandom.nextInt();
				sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
						secureRandom);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return sslContext;
		}

		@Override
		protected Void doInBackground() throws Exception {
			Socket chatSocket = null;
			try {
				// SSLSocketFactory sf = sslContext.getSocketFactory();
				// requesterSSLSocket = (SSLSocket)
				// sf.createSocket(remoteClientIP, initPort);
				Socket initSocket = new Socket(remoteClientIP, initPort);
				HelloMsg helloMsg = new HelloMsg();
				helloMsg.setMyName(loggedInUser);
				SSLServerSocket requesterWelcomeSSLSocket;

				// create a welcome socket on port
				SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
				requesterWelcomeSSLSocket = (SSLServerSocket) sf
						.createServerSocket(port);
				// Do not Require client authorization
				requesterWelcomeSSLSocket.setNeedClientAuth(true);
				requesterWelcomeSSLSocket.setSoTimeout(10000);
				// say hello and tell who I am
				networkUtils.send(helloMsg, initSocket);

				chatSocket = requesterWelcomeSSLSocket.accept();
				requesterWelcomeSSLSocket.close();
				// wait for 2s
				new ClientMessageHandler(chatSocket, Requestee).execute();
				networkUtils.send(message, chatSocket);
				if (message.getFlag().equals("CHAT_MSG")) {
					ChatMsg chatMsg = (ChatMsg) message;
					updateConversationPane(new Object[] {
							Calendar.getInstance().getTime().toString(),
							loggedInUser, chatMsg.getMessage() }, Requestee);
					String message = "Send to: "+Requestee+" Message:";
					logger.LogNormal(message);
				}
				else if (message.getFlag().equals("PIC_MSG")) {
					updateConversationPane(new Object[] {
							Calendar.getInstance().getTime().toString(),
							loggedInUser, "Picture Sent" }, Requestee);
				}
			} catch (SocketTimeoutException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

	}

	// this accepts the first client connection, then waits for additional
	// client connections
	class InitialClientConnectionAccepter extends SwingWorker<Void, Object> {
		private int port = 6678;

		@Override
		public Void doInBackground() {
			ServerSocket clientWelcomeSocket = null;
			boolean listening = true;
			try {
				clientWelcomeSocket = new ServerSocket(port);

			} catch (IOException e) {
				e.printStackTrace();
			}
			System.out
					.println("Client wait for another client to connect...\n");
			while (listening) {
				try {
					new ClientMessageAccepter(clientWelcomeSocket.accept())
							.execute();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return null;

		}
	}

	// this accepts the first client connection, then waits for additional
	// client connections
	class InitialServerMessageAccepter extends SwingWorker<Void, Object> {

		// port to listen
		private int port = 55678;

		@Override
		public Void doInBackground() {
			SSLContext sslContext = setupSSLContext();

			SSLServerSocket clientWelcomeSocket = null;
			boolean listening = true;
			try {
				// create a welcome socket on port
				SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
				clientWelcomeSocket = (SSLServerSocket) sf
						.createServerSocket(port);
				// Do not Require client authorization
				clientWelcomeSocket.setNeedClientAuth(true);
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.out.println("Client wait for Server List to connect...\n");
			while (listening) {
				try {
					new ServerListAccepter(clientWelcomeSocket.accept())
							.execute();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return null;

		}

		private SSLContext setupSSLContext() {

			SSLContext sslContext = null;
			try {
				TrustManagerFactory tmf = TrustManagerFactory
						.getInstance("SunX509");
				tmf.init(serverKeyStore);

				KeyManagerFactory kmf = KeyManagerFactory
						.getInstance("SunX509");
				kmf.init(clientKeyStore, passphrase.toCharArray());

				sslContext = SSLContext.getInstance("TLS");

				SecureRandom secureRandom = new SecureRandom();
				secureRandom.nextInt();
				sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
						secureRandom);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return sslContext;
		}
	}

	// accepts messages from other clients
	class ClientMessageAccepter extends SwingWorker<Void, Object> {

		private Socket connectionSocket = null;

		private NetworkUtils networkUtils;

		private KeyStore requesterKeyStore;
		private final int port = 6679;

		public ClientMessageAccepter(Socket socket) throws Exception {
			super();
			this.connectionSocket = socket;
			networkUtils = new NetworkUtils();
			confidentialitySecurityUtils = new ConfidentialitySecurityUtils();
			this.connectionSocket = socket;
			// this.server = server;
		}

		@Override
		public Void doInBackground() {
			Message msg;
			String RequesterName = null;
			SSLSocket chatSocket = null;
			try {
				msg = networkUtils.receive(connectionSocket);
				if (!msg.getFlag().equals("HELLO"))
					throw new Exception();

				int senderIndex = ((UserListTableModel) userListTable
						.getModel()).getIndexFromName(((HelloMsg) msg)
						.getMyName());
				RequesterName = (String) userListTable.getValueAt(senderIndex,
						0);
				String RequesterIP = (String) userListTable.getValueAt(
						senderIndex, 2);
				X509Certificate RequesterCertificate = (X509Certificate) userListTable
						.getValueAt(senderIndex, 1);
				setRequesterKeyStore(RequesterCertificate);
				SSLContext sslContext = setupSSLContext();
				SSLSocketFactory sf = sslContext.getSocketFactory();
				chatSocket = (SSLSocket) sf.createSocket(RequesterIP, port);
				new ClientMessageHandler(chatSocket, RequesterName).execute();

			} catch (Exception e) {
				e.printStackTrace();

			}

			return null;
		}

		private void setRequesterKeyStore(X509Certificate requesterCertificate)
				throws Exception {

			requesterKeyStore = KeyStore.getInstance("JKS");
			// GENERATE AN EMPTY clientKeyStore
			requesterKeyStore.load(null, passphrase.toCharArray());

			// Add the certificate
			requesterKeyStore.setCertificateEntry("RequesterCertificate",
					requesterCertificate);
		}

		private SSLContext setupSSLContext() {

			SSLContext sslContext = null;
			try {
				TrustManagerFactory tmf = TrustManagerFactory
						.getInstance("SunX509");
				tmf.init(requesterKeyStore);

				KeyManagerFactory kmf = KeyManagerFactory
						.getInstance("SunX509");
				kmf.init(clientKeyStore, passphrase.toCharArray());

				sslContext = SSLContext.getInstance("TLS");

				SecureRandom secureRandom = new SecureRandom();
				secureRandom.nextInt();
				sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
						secureRandom);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return sslContext;
		}
	}
	
	class ClientMessageHandler extends SwingWorker<Void, Object> {

		Socket chatSocket;
		String chater;

		public ClientMessageHandler(Socket chatSocket, String chater) {
			// TODO Auto-generated constructor stub
			this.chatSocket = chatSocket;
			this.chater = chater;
		}

		@Override
		protected Void doInBackground() {
			// TODO Auto-generated method stub
			socketMap.put(chater, chatSocket);
			Message msg;
			try {
				while (true) {
					msg = networkUtils.receive(chatSocket);
					if (msg.getFlag().equals("CHAT_MSG")) {
						ChatMsg chatMsg = (ChatMsg) msg;

						if(chatMsg.getMessage().length() > MAXFREEMSGLENGTH){
							if(!client.isPaidUser(chatMsg.getFromUserName())){
								JOptionPane.showMessageDialog(chatFrame, "Eve is trying to send you a Long Message!!!");
								logger.LogSevere("Unauthorized long message from "+chatMsg.getFromUserName()+"; IP Address:"+chatSocket.getInetAddress());
								continue;
							}
						}
						updateConversationPane(
								new Object[] {
										Calendar.getInstance().getTime()
												.toString(),

										chatMsg.getFromUserName(),
										chatMsg.getMessage() },
								chatMsg.getFromUserName());
						logger.LogNormal("Receive Message from: "+chatMsg.getFromUserName());
					}
					else if (msg.getFlag().equals("PROFILE_MSG")) {
						ProfileMsg profileMsg = (ProfileMsg) msg;
						initViewProfileInterface(profileMsg.getUserName(), profileMsg.getProfile());
						logger.LogNormal("Get profile message from "+profileMsg.getUserName()+"; IP Address:"+chatSocket.getInetAddress());
						isViewProfileWindowOpen = true;
					}
					else if (msg.getFlag().equals("REQUEST_PROFILE_MSG")) {
						RequestProfileMsg requestProfileMsg = (RequestProfileMsg) msg;
						ProfileMsg profileMsg = new ProfileMsg();
						profileMsg.setUserName(loggedInUser);
						if(profileSharingMap.get(requestProfileMsg.getFromUserName()) == null || profileSharingMap.get(requestProfileMsg.getFromUserName()).booleanValue() == false){
							profileMsg.setProfile(new Profile("None","Business","Of Your"));
							logger.LogSevere("Unauthorized profile access from "+requestProfileMsg.getFromUserName()+"; IP Address:"+chatSocket.getInetAddress());
						}
						else{
							profileMsg.setProfile(profile);
							logger.LogNormal("Authorized profile access from "+requestProfileMsg.getFromUserName()+"; IP Address:"+chatSocket.getInetAddress());
						}
						networkUtils.send(profileMsg, socketMap.get(requestProfileMsg.getFromUserName()));
					}
					else if (msg.getFlag().equals("PIC_MSG")) {
						PictureMsg picMsg = (PictureMsg) msg;
						

						if(!client.isPaidUser(picMsg.getFromUserName())){
							JOptionPane.showMessageDialog(chatFrame, "Eve is trying to send you a picture!!!");
							logger.LogSevere("Unauthorized picture message from "+picMsg.getFromUserName()+"; IP Address:"+chatSocket.getInetAddress());
							continue;
						}
						
						//pop up pic
						ImageIcon imageIcon = picMsg.getImage();
						JLabel label = new JLabel();
						label.setIcon(imageIcon);
						JPanel jp = new JPanel();
						jp.add(label);

						final JFrame picturePreviewFrame = new JFrame(
								"Picture from: " + picMsg.getFromUserName());
						logger.LogNormal("Receive picture from :"+picMsg.getFromUserName());
						picturePreviewFrame.setSize(400, 400);
						picturePreviewFrame.setLocationRelativeTo(null);
						// Add content to the window.
						picturePreviewFrame.add(jp);

						// Display the window.
						// picturePreviewFrame.pack();
						picturePreviewFrame.setVisible(true);
						
						updateConversationPane(
								new Object[] {
										Calendar.getInstance().getTime()
												.toString(),

										picMsg.getFromUserName(),
										"Picture received" },
										picMsg.getFromUserName());
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				socketMap.remove(chater);
				try {
					chatSocket.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return null;
		}

	}

	// accepts messages from other clients
	class ServerListAccepter extends SwingWorker<Void, Object> {
		private Socket connectionSocket = null;

		ServerListAccepter(Socket socket) {
			super();
			this.connectionSocket = socket;
		}

		@Override
		public Void doInBackground() {
			// NetworkUtils networkUtils = new NetworkUtils();

			ObjectInputStream ois = null;
			while (true) {

				// Message msgReceived = (Message)
				// networkUtils.receive(connectionSocket,null);
				Message msgReceived;
				try {
					ois = new ObjectInputStream(
							connectionSocket.getInputStream());
					msgReceived = (Message) ois.readObject();
					System.out.println(msgReceived.getFlag());
				} catch (IOException e) {
					e.printStackTrace();
					return null; // connection is cut so terminate current
									// thread
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
					continue;// discard corresponding message and continues loop
				}
				String flag = msgReceived.getFlag();
				if ("POST_USERLIST".equals(flag)) {
					// userlist update code here
					PostUserListMsgArrayList postUserList = (PostUserListMsgArrayList) msgReceived;
					// find out currently selected user
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					boolean foundUserInUserList;
					for (String tempUserName : socketMap.keySet()) {
						foundUserInUserList = false;
						for (User user : postUserList.getUserList()) {
							if (user.getUserName().equals(tempUserName)) {
								foundUserInUserList = true;
								break;
							}
						}
						if (foundUserInUserList == false) {
							socketMap.remove(tempUserName);
						}
					}
					
					int selectedRow = userListTable.getSelectedRow();
					String userName = null;
					if (selectedRow != -1) {
						userName = (String) userListTable.getValueAt(
								selectedRow, 0);
					}
					System.out.println("Userlist being updated");
					ArrayList<User> userList = (ArrayList<User>) postUserList
							.getUserList();
					//remove self from list
					for(int i = 0; i < userList.size(); i++){
						if(userList.get(i).getUserName().equals(loggedInUser)){
							userList.remove(i);
						}
					}
					Object[][] userListData = new Object[userList.size()][3];
					int indexToPreselect = -1;
					for (int i = 0; i < userList.size(); i++) {
						if (userName != null
								&& userList.get(i).getUserName()
										.equals(userName)) {
							indexToPreselect = i;
						}
						userListData[i][0] = userList.get(i).getUserName();
						userListData[i][1] = userList.get(i).getCertificate();
						userListData[i][2] = userList.get(i).getIpAddress();
					}
					TableModel updatedTableModel = new UserListTableModel(
							userListData);

					userListTable.setModel(updatedTableModel);

					if (indexToPreselect != -1) {
						ListSelectionModel model = userListTable
								.getSelectionModel();
						model.setSelectionInterval(indexToPreselect,
								indexToPreselect);
					}

				}
			}
		}
	}

	private void changeConversationPane(String username) {
		if (conversations.get(username) == null) {
			DefaultTableModel model = new DefaultTableModel() {
				private static final long serialVersionUID = 1L;

				public boolean isCellEditable(int nRow, int nCol) {
					return false;
				}
			};
			model.addColumn("Timestamp");
			model.addColumn("Username");
			model.addColumn("Message");

			conversations.put(username, model);

		}
		DefaultTableModel model = conversations.get(username);
		chatTable = new JTable(model);
		chatTable.setEnabled(false);
		chatScrollPane = new JScrollPane(chatTable);
		chatPanel.remove(1);
		chatPanel.add(chatScrollPane);
		chatPanel.repaint();
		chatPanel.revalidate();
	}

	private void updateConversationPane(Object[] rowData, String username) {
		if (conversations.get(username) == null) {
			DefaultTableModel model = new DefaultTableModel() {
				private static final long serialVersionUID = 1L;

				public boolean isCellEditable(int nRow, int nCol) {
					return false;
				}
			};
			model.addColumn("Timestamp");
			model.addColumn("Username");
			model.addColumn("Message");

			conversations.put(username, model);

		}
		DefaultTableModel model = conversations.get(username);

		model.insertRow(model.getRowCount(), rowData);
		chatTable = new JTable(model);
		chatTable.setEnabled(false);
		chatScrollPane = new JScrollPane(chatTable);
		chatPanel.remove(1);
		chatPanel.add(chatScrollPane);
		chatPanel.repaint();
		chatPanel.revalidate();

	}
}